using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace HenIT.CAGCompare
{
    public class GACList
    {
        #region Privates
        private readonly string GACDirectory = "\\c$\\Windows\\assembly\\GAC\\"; //default for .Net 1.x
        private readonly string GAC_32Directory = "\\c$\\Windows\\assembly\\GAC_32\\";
        private readonly string GAC_64Directory = "\\c$\\Windows\\assembly\\GAC_64\\";
        private readonly string GAC_MSILDirectory = "\\c$\\Windows\\assembly\\GAC_MSIL\\"; //default for .Net 2.x
        private readonly string GAC4_MSILDirectory = "\\c$\\Windows\\Microsoft.NET\\assembly\\GAC_MSIL\\"; //default for .Net 4.x
        private readonly string GAC4_32Directory = "\\c$\\Windows\\Microsoft.NET\\assembly\\GAC_32\\";
        private readonly string GAC4_64Directory = "\\c$\\Windows\\Microsoft.NET\\assembly\\GAC_64\\";
        #endregion

        #region Properties
        private List<GACEntry> assemblies = new List<GACEntry>();
        public List<GACEntry> Assemblies
        {
            get { return assemblies; }
            set { assemblies = value; }
        }

        private string machineName;
        public string MachineName
        {
            get { return machineName; }
            set { machineName = value; }
        }
        #endregion

        #region Methods
        public void LoadList(string machineName, string filter)
        {
            MachineName = machineName;
            if (filter.Length == 0)
                filter = "*";
            assemblies = new List<GACEntry>();
            GetGACFiles("\\\\" + machineName + GACDirectory, filter, "GAC");
            GetGACFiles("\\\\" + machineName + GAC_MSILDirectory, filter, "GAC_MSIL");
            GetGACFiles("\\\\" + machineName + GAC_32Directory, filter, "GAC_32");
            GetGACFiles("\\\\" + machineName + GAC_64Directory, filter, "GAC_64");
            GetGACFiles("\\\\" + machineName + GAC4_MSILDirectory, filter, "GAC4_MSIL");
            GetGACFiles("\\\\" + machineName + GAC4_32Directory, filter, "GAC4_32");
            GetGACFiles("\\\\" + machineName + GAC4_64Directory, filter, "GAC4_64");
        }
        private void GetGACFiles(string rootDirectory, string filter, string gacEntryType)
        {
            if (Directory.Exists(rootDirectory))
            {
                string[] assemblyDirectories = Directory.GetDirectories(rootDirectory, filter);
                foreach (string assemblyDirectory in assemblyDirectories)
                {
                    string[] versionTokenDirectories = Directory.GetDirectories(assemblyDirectory);
                    foreach (string versionTokenDirectory in versionTokenDirectories)
                    {
                        string[] assemblyFiles = Directory.GetFiles(versionTokenDirectory, "*.*"); ///There should only be one... Don't loose your head

                        GACEntry gacEntry = new GACEntry();
                        gacEntry.GacType = gacEntryType;
                        gacEntry.MachineName = MachineName;
                        gacEntry.AssemblyName = Path.GetFileName(assemblyDirectory);
                        gacEntry.AssemblyVersion = GetVersionFromVersionToken(versionTokenDirectory);
                        gacEntry.Culture = GetCultureFromVersionToken(versionTokenDirectory);
                        gacEntry.PublicKeyToken = GetTokenFromVersionToken(versionTokenDirectory);
                        gacEntry.AssemblyPath = assemblyFiles;
                        foreach (string assemblyDll in assemblyFiles)
                        {
                            if (assemblyDll.EndsWith(".dll") || assemblyDll.EndsWith(".exe"))
                            {
                                try
                                {
                                    FileInfo fi = new FileInfo(assemblyDll);
                                    gacEntry.FileSize = fi.Length;
                                    gacEntry.creationDate = fi.CreationTime; //date installed in GAC
                                }
                                catch (Exception ex)
                                {
                                    gacEntry.FileSize = 0;
                                    gacEntry.creationDate = new DateTime();
                                    System.Diagnostics.Trace.WriteLine(ex.ToString());
                                }
                            }
                        }
                        if (gacEntry.AssemblyPath.Length >0 && !assemblies.Contains(gacEntry)) //avoid duplicates 
                            assemblies.Add(gacEntry);
                    }
                }
            }
        }
        
        private string GetProperVersionToken(string versionTokenDirectory)
        {
            string tempName = System.IO.Path.GetFileName(versionTokenDirectory);
            if (tempName.StartsWith("v4.0_"))
                return tempName.Remove(0, 5);
            else
                return tempName;
        }
        private string GetTokenFromVersionToken(string versionToken)
        {
            string tempName = GetProperVersionToken(versionToken);
            tempName = tempName.Substring(tempName.LastIndexOf("_") + 1);
            return tempName;
        }
        private string GetCultureFromVersionToken(string versionToken)
        {
            string tempName = GetProperVersionToken(versionToken);
            tempName = tempName.Substring(tempName.IndexOf("_") + 1);
            if (tempName.StartsWith("_"))
                tempName = "neutral";
            else
                tempName = tempName.Substring(0, tempName.IndexOf("_"));
            return tempName;
        }
        private Version GetVersionFromVersionToken(string versionToken)
        {
            string tempName = GetProperVersionToken(versionToken);
            tempName = tempName.Substring(0, tempName.IndexOf("_"));
            Version v = new Version(tempName);
            return v;
        }

        public List<GACEntry> GetUnique(List<GACEntry> compareTo)
        {
            bool found = false;
            List<GACEntry> unique = new List<GACEntry>();
            foreach (GACEntry gacEntry in assemblies)
            {
                found = false;
                foreach (GACEntry compareEntry in compareTo)
                {
                    if (gacEntry.Equals(compareEntry))
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                    unique.Add(gacEntry);
            }
            return unique;
        }

        public List<GACEntry> GetUniqueVersion(List<GACEntry> compareTo)
        {
            bool found = false;
            bool foundVersion = false;
            List<GACEntry> unique = new List<GACEntry>();
            foreach (GACEntry gacEntry in assemblies)
            {
                found = false;
                foundVersion = false;
                foreach (GACEntry compareEntry in compareTo)
                {
                    if (gacEntry.AssemblyName == compareEntry.AssemblyName)
                    {
                        found = true;
                        if (gacEntry.AssemblyVersion.Equals(compareEntry.AssemblyVersion))
                        {
                            foundVersion = true;
                            break;
                        }
                    }
                }

                if (found && !foundVersion)
                    unique.Add(gacEntry);
            }
            return unique;
        } 
        #endregion
    }
}
